/***************************************************************************
 *   oboe.controllers.PerlinTerrain                                        *
 *   Copyright (C) 2011 by Felipe Manga                                    *
 *   lufeboma@gmail.com                                                    *
 *                                                                         *
 *   Permission is hereby granted, free of charge, to any person obtaining *
 *   a copy of this software and associated documentation files (the       *
 *   "Software"), to deal in the Software without restriction, including   *
 *   without limitation the rights to use, copy, modify, merge, publish,   *
 *   distribute, sublicense, and/or sell copies of the Software, and to    *
 *   permit persons to whom the Software is furnished to do so, subject to *
 *   the following conditions:                                             *
 *                                                                         *
 *   The above copyright notice and this permission notice shall be        *
 *   included in all copies or substantial portions of the Software.       *
 *                                                                         *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       *
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    *
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR     *
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
 *   OTHER DEALINGS IN THE SOFTWARE.                                       *
 ***************************************************************************/

package oboe.controllers
{
	import oboe.math.Number2D;
	import oboe.math.Number3D;
	import oboe.utils.RandomInt;
	
	public class PerlinTerrain extends GridController
	{
		private var heightMap:Vector.<Number>;
		private var heightMapWidth:int;
		private static const tmp:Number3D = new Number3D(0,0,0);
		
		public function PerlinTerrain( tiles:Number2D, keys:Number2D, textureCount:int, textureSize:int )
		{
			setupHeightMap( tiles, keys, 1/textureCount );
			super( heightMap, tiles, new Number2D(1,1), textureCount, textureSize, false );
			
			for( var y:int = 0; y < tiles.y; ++y )
			{
				for( var x:int = 0; x < tiles.x; ++x )
				{
					this.setTileTexture( x, y, int(this.getTileHeightQuick(x, y)*textureCount) );
				}
			}
		}
		
		private function setupHeightMap( tiles:Number2D, keys:Number2D, waterLevel:Number ):void
		{
			var range:Number2D = new Number2D( Number.MAX_VALUE, Number.MIN_VALUE );
			RandomInt.srand( keys.x*0xFFFF+keys.y );
			tiles.floor();
			this.heightMapWidth = tiles.x+1;
			heightMap = new Vector.<Number>( (tiles.x+1)*(tiles.y+1), true );
			for( var y:int = 0; y <= tiles.y; ++y )
			{
				for( var x:int = 0; x <= tiles.x; ++x )
				{
					this.heightMap[ y*this.heightMapWidth+x ] = 0;
				}
			}
//			this.heightMap[ 0 ] = 1;
//			this.heightMap[ 1 ] = 1;
//			this.heightMap[ 3*tiles.x ] = 1;
//			return;
			
			perlin( 1, 0,0, tiles.x+1, tiles.y+1, range );
			
			var f:Number = 1/(range.y - range.x);
			if( waterLevel < 1 )
			{
				for( y = 0; y <= tiles.y; ++y )
				{
					for( x = 0; x <= tiles.x; ++x )
					{
						var h:Number = this.heightMap[ y*this.heightMapWidth+x ];
						h = (h-range.x) * f;
						if( h < waterLevel ) h = 0;
						else h = (h-waterLevel)/(1-waterLevel);
						this.heightMap[ y*this.heightMapWidth+x ] = h;
					}
				}
			}
			else
			{
				for( y = 0; y <= tiles.y; ++y )
				{
					for( x = 0; x <= tiles.x; ++x )
					{
						h = this.heightMap[ y*this.heightMapWidth+x ];
						h = (h-range.x) * f;
						this.heightMap[ y*this.heightMapWidth+x ] = h;
					}
				}
			}
		}
		
		private function perlin( it:int, minX:int, minY:int, maxX:int, maxY:int, range:Number2D ):void
		{
			var r:Number;
			var t:Number;
			r = (RandomInt.randf())/(it); // *Math.sin(1.5708/(it*it));
			t = this.heightMap[ minY*this.heightMapWidth+minX ] + r;
			
			for( var y:int = minY; y < maxY; ++y )
			{
				for( var x:int = minX; x < maxX; ++x )
				{
					this.heightMap[ y*this.heightMapWidth+x ] = t;  
				}
			}
			++it;
			x = (minX+maxX)*0.5;
			y = (minY+maxY)*0.5;
			if( x <= minX || y <= minY )
			{
				if( t > range.y ) range.y = t;
				if( t < range.x ) range.x = t;
				return;
			}
			perlin( it, minX, minY, x, y, range );
			perlin( it, minX, y, x, maxY, range );
			perlin( it, x, minY, maxX, y, range );
			perlin( it, x, y, maxX, maxY, range );
		}
	}
}